use core::marker::PhantomData;

use crate::uart::Uart;
use cmsis_rtos2_rs::{osWaitForever, static_mem::EventFlags};
use utils::ByteBuffer;

const FLAG_TC: u32 = 0x01;
const FLAG_IDLE: u32 = 0x02;

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
    Timeout,
    Over,
}

pub struct UartContext<const RECV_BUF_SIZE: usize, U: Uart> {
    recv_buf: ByteBuffer<RECV_BUF_SIZE>,
    event_flags: EventFlags,
    _u: PhantomData<U>,
}

impl<const RECV_BUF_SIZE: usize, U: Uart> UartContext<RECV_BUF_SIZE, U> {
    pub const fn uninit() -> Self {
        Self {
            recv_buf: ByteBuffer::const_new(),
            event_flags: EventFlags::uninit(),
            _u: PhantomData,
        }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        self.event_flags.init_once(name);
    }

    pub fn write_read_until(
        &'static self,
        write_data: &'static [u8],
        until_data: &'static [u8],
        timeout: u32,
    ) -> Result<&'static [u8], Error> {
        let recv_buf = unsafe { self.recv_buf.as_mut_slice() };
        U::recv_with_dma(recv_buf);

        U::write_with_dma(write_data);
        _ = self.event_flags.wait_any_flags(FLAG_TC, osWaitForever);

        let mut index = 0;
        loop {
            let rx_index = U::rx_index();
            if rx_index == index {
                self.event_flags
                    .wait_any_flags(FLAG_IDLE, timeout)
                    .map_err(|_| Error::Timeout)?;
                continue;
            } else if rx_index < index {
                return Err(Error::Over);
            }
            
            index = rx_index;
            let rx_data = &recv_buf[..rx_index];
            if rx_data.ends_with(until_data) {
                return Ok(rx_data);
            }
        }
    }

    pub fn on_tc(&self) {
        self.event_flags.set_flags(FLAG_TC);
    }

    pub fn on_idle(&self) {
        self.event_flags.set_flags(FLAG_IDLE);
    }
}
